package it.giulio.oracle;

import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.Date;
import java.sql.ParameterMetaData;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Map;
import java.util.StringTokenizer;

import org.apache.log4j.Logger;

public class OracleLoggablePreparedCall implements CallableStatement {
    // Variabile per il logger
    //private LoggerWrapper loggerWrapper = LoggerWrapper.getInstance();
	private Logger loggerWrapper = Logger.getLogger(OracleLoggablePreparedCall.class);

    private CallableStatement wrappedStatement;
    private String sqlTemplate;
    private String batchSQLString = "";
    private ArrayList parameterValues;

    public OracleLoggablePreparedCall(Connection connection, String sql) throws SQLException {
        wrappedStatement = connection.prepareCall(sql);
        sqlTemplate = sql;
        parameterValues = new ArrayList();
    }

    public OracleLoggablePreparedCall(Connection connection, String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        wrappedStatement = connection.prepareCall(sql, resultSetType, resultSetConcurrency);
        sqlTemplate = sql;
        parameterValues = new ArrayList();
    }

    public OracleLoggablePreparedCall(Connection connection, String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        wrappedStatement = connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
        sqlTemplate = sql;
        parameterValues = new ArrayList();
    }

    /**
     * Saves the parameter value <code>obj</code> for the specified <code>position</code> for use in logging output
     *
     * @param position position (starting at 1) of the parameter to save
     * @param obj java.lang.Object the parameter value to save
     */
    private void saveParamValue(int position, Object obj) {
        String strValue;

        if (obj instanceof String || obj instanceof Date) {
            // if we have a String or Date , include '' in the saved value
            strValue = "'" + obj + "'";
        } else {
            if (obj == null) {
                // convert null to the string null
                strValue = "null";
            } else {
                // unknown object (includes all Numbers), just call toString
                strValue = obj.toString();
            }
        }

        // if we are setting a position larger than current size of parameterValues, first make it larger
        while (position >= parameterValues.size()) {
            parameterValues.add(null);
        }

        // save the parameter
        parameterValues.set(position, strValue);
    }

    public Object getObject(String p0, Map p1) throws SQLException {
        return wrappedStatement.getObject(p0, p1);
    }

    public Object getObject(int p0, Map p1) throws SQLException {
        return wrappedStatement.getObject(p0, p1);
    }

    public Object getObject(int p0) throws SQLException {
        return wrappedStatement.getObject(p0);
    }

    public Object getObject(String p0) throws SQLException {
        return wrappedStatement.getObject(p0);
    }

    public boolean getBoolean(String p0) throws SQLException {
        return wrappedStatement.getBoolean(p0);
    }

    public boolean getBoolean(int p0) throws SQLException {
        return wrappedStatement.getBoolean(p0);
    }

    public byte getByte(int p0) throws SQLException {
        return wrappedStatement.getByte(p0);
    }

    public byte getByte(String p0) throws SQLException {
        return wrappedStatement.getByte(p0);
    }

    public short getShort(String p0) throws SQLException {
        return wrappedStatement.getShort(p0);
    }

    public short getShort(int p0) throws SQLException {
        return wrappedStatement.getShort(p0);
    }

    public int getInt(int p0) throws SQLException {
        return wrappedStatement.getInt(p0);
    }

    public int getInt(String p0) throws SQLException {
        return wrappedStatement.getInt(p0);
    }

    public long getLong(String p0) throws SQLException {
        return wrappedStatement.getLong(p0);
    }

    public long getLong(int p0) throws SQLException {
        return wrappedStatement.getLong(p0);
    }

    public float getFloat(int p0) throws SQLException {
        return wrappedStatement.getFloat(p0);
    }

    public float getFloat(String p0) throws SQLException {
        return wrappedStatement.getFloat(p0);
    }

    public double getDouble(int p0) throws SQLException {
        return wrappedStatement.getDouble(p0);
    }

    public double getDouble(String p0) throws SQLException {
        return wrappedStatement.getDouble(p0);
    }

    public byte[] getBytes(int p0) throws SQLException {
        return wrappedStatement.getBytes(p0);
    }

    public byte[] getBytes(String p0) throws SQLException {
        return wrappedStatement.getBytes(p0);
    }

    public Array getArray(String p0) throws SQLException {
        return wrappedStatement.getArray(p0);
    }

    public Array getArray(int p0) throws SQLException {
        return wrappedStatement.getArray(p0);
    }

    public URL getURL(int p0) throws SQLException {
        return wrappedStatement.getURL(p0);
    }

    public URL getURL(String p0) throws SQLException {
        return wrappedStatement.getURL(p0);
    }

    public void setBoolean(String p0, boolean p1) throws SQLException {
        wrappedStatement.setBoolean(p0, p1);

        //saveParamValue(p0, new Boolean(p1));
    }

    public void setByte(String p0, byte p1) throws SQLException {
        wrappedStatement.setByte(p0, p1);

        //saveParamValue(p0, p1);
    }

    public void setDouble(String p0, double p1) throws SQLException {
        wrappedStatement.setDouble(p0, p1);

        //saveParamValue(p0, p1);
    }

    public void setFloat(String p0, float p1) throws SQLException {
        wrappedStatement.setFloat(p0, p1);

        //saveParamValue(p0, p1);
    }

    public void setInt(String p0, int p1) throws SQLException {
        wrappedStatement.setInt(p0, p1);

        //saveParamValue(p0, p1);
    }

    public void setLong(String p0, long p1) throws SQLException {
        wrappedStatement.setLong(p0, p1);

        //saveParamValue(p0, p1);
    }

    public void setShort(String p0, short p1) throws SQLException {
        wrappedStatement.setShort(p0, p1);

        //saveParamValue(p0, p1);
    }

    public void setTimestamp(String p0, Timestamp p1) throws SQLException {
        wrappedStatement.setTimestamp(p0, p1);

        //saveParamValue(p0, p1);
    }

    public void setTimestamp(String p0, Timestamp p1, Calendar p2) throws SQLException {
        wrappedStatement.setTimestamp(p0, p1, p2);

        //saveParamValue(p0, p1);
    }

    public Ref getRef(int p0) throws SQLException {
        return wrappedStatement.getRef(p0);
    }

    public Ref getRef(String p0) throws SQLException {
        return wrappedStatement.getRef(p0);
    }

    public void setURL(String p0, URL p1) throws SQLException {
        wrappedStatement.setURL(p0, p1);

        //saveParamValue(p0, p1);
    }

    public Date getDate(String p0, Calendar p1) throws SQLException {
        return wrappedStatement.getDate(p0, p1);
    }

    public Date getDate(int p0, Calendar p1) throws SQLException {
        return wrappedStatement.getDate(p0, p1);
    }

    public Date getDate(String p0) throws SQLException {
        return wrappedStatement.getDate(p0);
    }

    public Date getDate(int p0) throws SQLException {
        return wrappedStatement.getDate(p0);
    }

    public BigDecimal getBigDecimal(String p0) throws SQLException {
        return wrappedStatement.getBigDecimal(p0);
    }

    public BigDecimal getBigDecimal(int p0) throws SQLException {
        return wrappedStatement.getBigDecimal(p0);
    }

    public BigDecimal getBigDecimal(int p0, int p1) throws SQLException {
        return wrappedStatement.getBigDecimal(p0, p1);
    }

    public Blob getBlob(String p0) throws SQLException {
        return wrappedStatement.getBlob(p0);
    }

    public Blob getBlob(int p0) throws SQLException {
        return wrappedStatement.getBlob(p0);
    }

    public Clob getClob(String p0) throws SQLException {
        return wrappedStatement.getClob(p0);
    }

    public Clob getClob(int p0) throws SQLException {
        return wrappedStatement.getClob(p0);
    }

    public String getString(int p0) throws SQLException {
        return wrappedStatement.getString(p0);
    }

    public String getString(String p0) throws SQLException {
        return wrappedStatement.getString(p0);
    }

    public Time getTime(int p0) throws SQLException {
        return wrappedStatement.getTime(p0);
    }

    public Time getTime(String p0) throws SQLException {
        return wrappedStatement.getTime(p0);
    }

    public Time getTime(String p0, Calendar p1) throws SQLException {
        return wrappedStatement.getTime(p0, p1);
    }

    public Time getTime(int p0, Calendar p1) throws SQLException {
        return wrappedStatement.getTime(p0, p1);
    }

    public Timestamp getTimestamp(String p0) throws SQLException {
        return wrappedStatement.getTimestamp(p0);
    }

    public Timestamp getTimestamp(int p0, Calendar p1) throws SQLException {
        return wrappedStatement.getTimestamp(p0, p1);
    }

    public Timestamp getTimestamp(String p0, Calendar p1) throws SQLException {
        return wrappedStatement.getTimestamp(p0, p1);
    }

    public Timestamp getTimestamp(int p0) throws SQLException {
        return wrappedStatement.getTimestamp(p0);
    }

    public void registerOutParameter(int p0, int p1, String p2) throws SQLException {
        wrappedStatement.registerOutParameter(p0, p1, p2);
    }

    public void registerOutParameter(String p0, int p1, int p2) throws SQLException {
        wrappedStatement.registerOutParameter(p0, p1, p2);
    }

    public void registerOutParameter(String p0, int p1, String p2) throws SQLException {
        wrappedStatement.registerOutParameter(p0, p1, p2);
    }

    public void registerOutParameter(int p0, int p1, int p2) throws SQLException {
        wrappedStatement.registerOutParameter(p0, p1, p2);
    }

    public void registerOutParameter(int p0, int p1) throws SQLException {
        wrappedStatement.registerOutParameter(p0, p1);
    }

    public void registerOutParameter(String p0, int p1) throws SQLException {
        wrappedStatement.registerOutParameter(p0, p1);
    }

    public void setAsciiStream(String p0, InputStream p1, int p2) throws SQLException {
        wrappedStatement.setAsciiStream(p0, p1, p2);

        //saveParamValue(p0, p1);
    }

    public void setBigDecimal(String p0, BigDecimal p1) throws SQLException {
        wrappedStatement.setBigDecimal(p0, p1);

        //saveParamValue(p0, p1);
    }

    public void setBinaryStream(String p0, InputStream p1, int p2) throws SQLException {
        wrappedStatement.setBinaryStream(p0, p1, p2);

        //saveParamValue(p0, p1);
    }

    public void setBytes(String p0, byte[] p1) throws SQLException {
        wrappedStatement.setBytes(p0, p1);

        //saveParamValue(p0, p1);
    }

    public void setCharacterStream(String p0, Reader p1, int p2) throws SQLException {
        wrappedStatement.setCharacterStream(p0, p1, p2);

        //saveParamValue(p0, p1);
    }

    public void setDate(String p0, Date p1, Calendar p2) throws SQLException {
        wrappedStatement.setDate(p0, p1, p2);

        //saveParamValue(p0, p1);
    }

    public void setDate(String p0, Date p1) throws SQLException {
        wrappedStatement.setDate(p0, p1);

        //saveParamValue(p0, p1);
    }

    public void setNull(String p0, int p1) throws SQLException {
        wrappedStatement.setNull(p0, p1);

        //saveParamValue(p0, p1);
    }

    public void setNull(String p0, int p1, String p2) throws SQLException {
        wrappedStatement.setNull(p0, p1, p2);

        //saveParamValue(p0, p1);
    }

    public void setObject(String p0, Object p1) throws SQLException {
        wrappedStatement.setObject(p0, p1);

        //saveParamValue(p0, p1);
    }

    public void setObject(String p0, Object p1, int p2) throws SQLException {
        wrappedStatement.setObject(p0, p1, p2);

        //saveParamValue(p0, p1);
    }

    public void setObject(String p0, Object p1, int p2, int p3) throws SQLException {
        wrappedStatement.setObject(p0, p1, p2, p3);

        //saveParamValue(p0, p1);
    }

    public void setString(String p0, String p1) throws SQLException {
        wrappedStatement.setString(p0, p1);

        //saveParamValue(p0, p1);
    }

    public void setTime(String p0, Time p1, Calendar p2) throws SQLException {
        wrappedStatement.setTime(p0, p1, p2);

        //saveParamValue(p0, p1);
    }

    public void setTime(String p0, Time p1) throws SQLException {
        wrappedStatement.setTime(p0, p1);

        //saveParamValue(p0, p1);
    }

    public boolean wasNull() throws SQLException {
        return wrappedStatement.wasNull();
    }

    public void setBoolean(int p0, boolean p1) throws SQLException {
        wrappedStatement.setBoolean(p0, p1);
        saveParamValue(p0, new Boolean(p1));
    }

    public void setByte(int p0, byte p1) throws SQLException {
        wrappedStatement.setByte(p0, p1);
        saveParamValue(p0, new Byte(p1));
    }

    public void setDouble(int p0, double p1) throws SQLException {
        wrappedStatement.setDouble(p0, p1);
        saveParamValue(p0, new Double(p1));
    }

    public void setFloat(int p0, float p1) throws SQLException {
        wrappedStatement.setFloat(p0, p1);
        saveParamValue(p0, new Float(p1));
    }

    public void setInt(int p0, int p1) throws SQLException {
        wrappedStatement.setInt(p0, p1);
        saveParamValue(p0, new Integer(p1));
    }

    public void setLong(int p0, long p1) throws SQLException {
        wrappedStatement.setLong(p0, p1);
        saveParamValue(p0, new Long(p1));
    }

    public void setShort(int p0, short p1) throws SQLException {
        wrappedStatement.setShort(p0, p1);
        saveParamValue(p0, new Short(p1));
    }

    public void setTimestamp(int p0, Timestamp p1, Calendar p2) throws SQLException {
        wrappedStatement.setTimestamp(p0, p1, p2);
        saveParamValue(p0, p1);
    }

    public void setTimestamp(int p0, Timestamp p1) throws SQLException {
        wrappedStatement.setTimestamp(p0, p1);
        saveParamValue(p0, p1);
    }

    public void setURL(int p0, URL p1) throws SQLException {
        wrappedStatement.setURL(p0, p1);
        saveParamValue(p0, p1);
    }

    public boolean execute() throws SQLException {
        loggerWrapper.debug(getQueryString());

        return wrappedStatement.execute();
    }

    public void setAsciiStream(int p0, InputStream p1, int p2) throws SQLException {
        wrappedStatement.setAsciiStream(p0, p1, p2);
        saveParamValue(p0, p1);
    }

    public void setBigDecimal(int p0, BigDecimal p1) throws SQLException {
        wrappedStatement.setBigDecimal(p0, p1);
        saveParamValue(p0, p1);
    }

    public void setBinaryStream(int p0, InputStream p1, int p2) throws SQLException {
        wrappedStatement.setBinaryStream(p0, p1, p2);
        saveParamValue(p0, p1);
    }

    public void setBytes(int p0, byte[] p1) throws SQLException {
        wrappedStatement.setBytes(p0, p1);
        saveParamValue(p0, p1);
    }

    public void setCharacterStream(int p0, Reader p1, int p2) throws SQLException {
        wrappedStatement.setCharacterStream(p0, p1, p2);
        saveParamValue(p0, p1);
    }

    public void setDate(int p0, Date p1, Calendar p2) throws SQLException {
        wrappedStatement.setDate(p0, p1, p2);
        saveParamValue(p0, p1);
    }

    public void setDate(int p0, Date p1) throws SQLException {
        wrappedStatement.setDate(p0, p1);
        saveParamValue(p0, p1);
    }

    public void setNull(int p0, int p1, String p2) throws SQLException {
        wrappedStatement.setNull(p0, p1, p2);
        saveParamValue(p0, null);
    }

    public void setNull(int p0, int p1) throws SQLException {
        wrappedStatement.setNull(p0, p1);
        saveParamValue(p0, null);
    }

    public void setObject(int p0, Object p1, int p2, int p3) throws SQLException {
        wrappedStatement.setObject(p0, p1, p2, p3);
        saveParamValue(p0, p1);
    }

    public void setObject(int p0, Object p1, int p2) throws SQLException {
        wrappedStatement.setObject(p0, p1, p2);
        saveParamValue(p0, p1);
    }

    public void setObject(int p0, Object p1) throws SQLException {
        wrappedStatement.setObject(p0, p1);
        saveParamValue(p0, p1);
    }

    public void setString(int p0, String p1) throws SQLException {
        wrappedStatement.setString(p0, p1);
        saveParamValue(p0, p1);
    }

    public void setTime(int p0, Time p1) throws SQLException {
        wrappedStatement.setTime(p0, p1);
        saveParamValue(p0, p1);
    }

    public void setTime(int p0, Time p1, Calendar p2) throws SQLException {
        wrappedStatement.setTime(p0, p1, p2);
        saveParamValue(p0, p1);
    }

    public void addBatch() throws SQLException {
        wrappedStatement.addBatch();
        batchSQLString += (getQueryString() + ";\n");
    }

    public void clearParameters() throws SQLException {
        wrappedStatement.clearParameters();
        batchSQLString = "";
    }

    public ResultSet executeQuery() throws SQLException {
        loggerWrapper.debug(getQueryString());

        return wrappedStatement.executeQuery();
    }

    public int executeUpdate() throws SQLException {
        loggerWrapper.debug(getQueryString());

        return wrappedStatement.executeUpdate();
    }

    public ResultSetMetaData getMetaData() throws SQLException {
        return wrappedStatement.getMetaData();
    }

    public ParameterMetaData getParameterMetaData() throws SQLException {
        return wrappedStatement.getParameterMetaData();
    }

    public void setArray(int p0, Array p1) throws SQLException {
        wrappedStatement.setArray(p0, p1);
        saveParamValue(p0, p1);
    }

    public void setBlob(int p0, Blob p1) throws SQLException {
        wrappedStatement.setBlob(p0, p1);
        saveParamValue(p0, p1);
    }

    public void setClob(int p0, Clob p1) throws SQLException {
        wrappedStatement.setClob(p0, p1);
        saveParamValue(p0, p1);
    }

    public void setRef(int p0, Ref p1) throws SQLException {
        wrappedStatement.setRef(p0, p1);
        saveParamValue(p0, p1);
    }

    public void setUnicodeStream(int p0, InputStream p1, int p2) throws SQLException {
        wrappedStatement.setUnicodeStream(p0, p1, p2);
        saveParamValue(p0, p1);
    }

    public void close() throws SQLException {
        wrappedStatement.close();
    }

    public boolean execute(String p0, int p1) throws SQLException {
        loggerWrapper.debug(p0);

        return wrappedStatement.execute(p0, p1);
    }

    public boolean execute(String p0, String[] p1) throws SQLException {
        loggerWrapper.debug(p0);

        return wrappedStatement.execute(p0, p1);
    }

    public boolean execute(String p0, int[] p1) throws SQLException {
        loggerWrapper.debug(p0);

        return wrappedStatement.execute(p0, p1);
    }

    public boolean execute(String p0) throws SQLException {
        loggerWrapper.debug(p0);

        return wrappedStatement.execute(p0);
    }

    public void addBatch(String p0) throws SQLException {
        wrappedStatement.addBatch(p0);
        batchSQLString += (p0 + ";\n");
    }

    public ResultSet executeQuery(String p0) throws SQLException {
        loggerWrapper.debug(p0);

        return wrappedStatement.executeQuery(p0);
    }

    public int executeUpdate(String p0, int p1) throws SQLException {
        loggerWrapper.debug(p0);

        return wrappedStatement.executeUpdate(p0, p1);
    }

    public int executeUpdate(String p0, int[] p1) throws SQLException {
        loggerWrapper.debug(p0);

        return wrappedStatement.executeUpdate(p0, p1);
    }

    public int executeUpdate(String p0, String[] p1) throws SQLException {
        loggerWrapper.debug(p0);

        return wrappedStatement.executeUpdate(p0, p1);
    }

    public int executeUpdate(String p0) throws SQLException {
        loggerWrapper.debug(p0);

        return wrappedStatement.executeUpdate(p0);
    }

    public void cancel() throws SQLException {
        wrappedStatement.cancel();
    }

    public void clearBatch() throws SQLException {
        wrappedStatement.clearBatch();
    }

    public void clearWarnings() throws SQLException {
        wrappedStatement.clearWarnings();
    }

    public int[] executeBatch() throws SQLException {
        loggerWrapper.debug(batchSQLString);

        return wrappedStatement.executeBatch();
    }

    public Connection getConnection() throws SQLException {
        return wrappedStatement.getConnection();
    }

    public int getFetchDirection() throws SQLException {
        return wrappedStatement.getFetchDirection();
    }

    public int getFetchSize() throws SQLException {
        return wrappedStatement.getFetchSize();
    }

    public ResultSet getGeneratedKeys() throws SQLException {
        return wrappedStatement.getGeneratedKeys();
    }

    public int getMaxFieldSize() throws SQLException {
        return wrappedStatement.getMaxFieldSize();
    }

    public int getMaxRows() throws SQLException {
        return wrappedStatement.getMaxRows();
    }

    public boolean getMoreResults() throws SQLException {
        return wrappedStatement.getMoreResults();
    }

    public boolean getMoreResults(int p0) throws SQLException {
        return wrappedStatement.getMoreResults(p0);
    }

    public int getQueryTimeout() throws SQLException {
        return wrappedStatement.getQueryTimeout();
    }

    public ResultSet getResultSet() throws SQLException {
        return wrappedStatement.getResultSet();
    }

    public int getResultSetConcurrency() throws SQLException {
        return wrappedStatement.getResultSetConcurrency();
    }

    public int getResultSetHoldability() throws SQLException {
        return wrappedStatement.getResultSetHoldability();
    }

    public int getResultSetType() throws SQLException {
        return wrappedStatement.getResultSetType();
    }

    public int getUpdateCount() throws SQLException {
        return wrappedStatement.getUpdateCount();
    }

    public SQLWarning getWarnings() throws SQLException {
        return wrappedStatement.getWarnings();
    }

    public void setCursorName(String p0) throws SQLException {
        wrappedStatement.setCursorName(p0);
    }

    public void setEscapeProcessing(boolean p0) throws SQLException {
        wrappedStatement.setEscapeProcessing(p0);
    }

    public void setFetchDirection(int p0) throws SQLException {
        wrappedStatement.setFetchDirection(p0);
    }

    public void setFetchSize(int p0) throws SQLException {
        wrappedStatement.setFetchSize(p0);
    }

    public void setMaxFieldSize(int p0) throws SQLException {
        wrappedStatement.setMaxFieldSize(p0);
    }

    public void setMaxRows(int p0) throws SQLException {
        wrappedStatement.setMaxRows(p0);
    }

    public void setQueryTimeout(int p0) throws SQLException {
        wrappedStatement.setQueryTimeout(p0);
    }

    /**
     * Returns the sql statement string (question marks replaced with set parameter values)
     * that will be (or has been) executed by the {@link java.sql.PreparedStatement PreparedStatement} that this
     * <code>LoggablePreparedStatement</code> is a wrapper for.
     * <p>
     * @return java.lang.String the statemant represented by this <code>LoggablePreparedStatement</code>
     */
    public String getQueryString() {
        StringBuffer buf = new StringBuffer();
        int qMarkCount = 0;
        ArrayList chunks = new ArrayList();
        StringTokenizer tok = new StringTokenizer(sqlTemplate + " ", "?");

        while (tok.hasMoreTokens()) {
            String oneChunk = tok.nextToken();
            buf.append(oneChunk);

            try {
                Object value;

                if (parameterValues.size() > (1 + qMarkCount)) {
                    value = parameterValues.get(1 + qMarkCount++);
                } else {
                    if (tok.hasMoreTokens()) {
                        value = null;
                    } else {
                        value = "";
                    }
                }

                buf.append("" + value);
            } catch (Throwable e) {
                buf.append("ERROR WHEN PRODUCING QUERY STRING FOR LOG." + e.toString());

                // catch this without whining, if this fails the only thing wrong is probably this class
            }
        }

        return buf.toString().trim();
    }

}